package utils

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"
	"yuanfilesys/dao"

	"github.com/gin-gonic/gin"
)

// 获取可用的文件名或文件夹名
func GetAvailableName(originalName string, parentID int, userID uint, isDir bool) string {
	//检查名称是否已存在
	var count int64
	//检查原始名称是否可用，名称可用，直接返回
	dao.DB.Model(&dao.FileNode{}).
		Where("parentid = ? AND userid = ? AND name = ?", parentID, userID, originalName).
		Count(&count)
	if count == 0 {
		return originalName
	}
	//名称不可用
	//提取文件名和扩展名（例如："test.jpg" → "test" + ".jpg"）
	name := originalName
	ext := ""
	if !isDir {
		dotIndex := strings.LastIndex(originalName, ".")
		if dotIndex >= 0 {
			name = originalName[:dotIndex]
			ext = originalName[dotIndex:]
		}
	}
	//尝试添加序号 自动生成不与现有文件 / 文件夹重名的新名称，通过在原始名称后添加递增序号（如 (1), (2)）来实现
	for i := 1; i < 1000; i++ {
		newName := fmt.Sprintf("%s (%d)%s", name, i, ext)
		dao.DB.Model(&dao.FileNode{}).
			Where("parentid = ? AND userid = ? AND name = ?", parentID, userID, newName).
			Count(&count)

		if count == 0 {
			return newName
		}
	}
	//如果尝试了1000次仍失败，则添加随机字符串
	randomBytes := make([]byte, 4)
	rand.Read(randomBytes)
	randomStr := hex.EncodeToString(randomBytes)
	return fmt.Sprintf("%s_%s%s", name, randomStr, ext)
}

// 构建路径
func ConstructPath(parentPath, name string) string {
	return filepath.Join(parentPath, name)
}

// 获取文件存储路径
func GetStoragePath(userID uint, username, filePath string) string {
	// 确保路径不以斜杠开头
	filePath = strings.TrimPrefix(filePath, "/")

	// 构建用户目录: id_username
	userDir := fmt.Sprintf("%d_%s", userID, username)

	// 返回完整路径: data/id_username/filePath
	return filepath.Join("data", userDir, filePath)
}

// 获取带版本号的文件存储路径
func GetStoragePathWithVersion(username, filePath string, version int) string {
	//规范化路径
	filePath = filepath.Clean(filePath)
	// 分解路径组件
	dir, base := filepath.Split(filePath)
	//获取文件名的扩展名
	ext := filepath.Ext(base)
	//从base中移除扩展名部分，得到纯文件名
	name := strings.TrimSuffix(base, ext)
	// 构建版本化文件名
	versionedBase := fmt.Sprintf("%s_v%d%s", name, version, ext)
	// 使用Join安全拼接路径
	return filepath.Join("data", username, dir, versionedBase)
}

// 构建存储路径（格式：data/id_username/path）
func BuildStoragePath(user *dao.User, filePath string) string {
	userDir := fmt.Sprintf("%d_%s", user.ID, user.Username)
	return filepath.Join("data", userDir, filePath)
}

// SaveFile保存上传的文件到指定路径
func SaveFile(c *gin.Context, dst string) error {
	//获取上传的文件
	file, _, err := c.Request.FormFile("file")
	if err != nil {
		return fmt.Errorf("获取上传文件失败: %w", err)
	}
	defer file.Close()

	//创建目标目录
	dir := filepath.Dir(dst)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return fmt.Errorf("创建目录失败: %w", err)
	}
	//创建目标文件
	out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return fmt.Errorf("创建目标文件失败: %w", err)
	}
	defer out.Close()
	//复制文件内容
	if _, err := io.Copy(out, file); err != nil {
		//如果复制失败，尝试删除可能不完整的文件
		os.Remove(dst)
		return fmt.Errorf("复制文件内容失败: %w", err)
	}
	return nil
}

// DeleteFile删除文件
func DeleteFile(path string) error {
	// 检查文件是否存在
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return nil
	}
	// 删除文件
	return os.Remove(path)
}

// CopyFile复制文件，先创建文件，再进行复制
func CopyFile(src, dst string) error {
	// 打开源文件
	sourceFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sourceFile.Close()

	// 创建目标目录
	dir := filepath.Dir(dst)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	// 创建目标文件
	destinationFile, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer destinationFile.Close()

	// 复制文件内容
	_, err = io.Copy(destinationFile, sourceFile)
	return err
}

// RenameFile 重命名文件
func RenameFile(oldPath, newPath string) error {
	//filepath.Dir(newPath) 会自动提取路径中的目录部分，并去掉文件名
	dir := filepath.Dir(newPath)

	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}

	// 重命名文件
	return os.Rename(oldPath, newPath)
}

// FileExists 检查文件是否存在
func FileExists(path string) bool {
	//检查文件或目录是否存在
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}

// GetCurrentTime 获取当前时间
func GetCurrentTime() time.Time {
	return time.Now()
}

// GetUsernameByID 通过用户ID获取用户名
func GetUsernameByID(userID uint) string {
	var user dao.User
	if err := dao.DB.Where("id = ?", userID).First(&user).Error; err != nil {
		return ""
	}
	return user.Username
}
